using System;
using System.Linq;
using System.Custom;
using System.Drawing;
using System.Xml.Linq;
using System.Collections;
using System.Windows.Forms;
using System.Custom.Window;
using System.Custom.Drawing;
using System.Drawing.Drawing2D;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace CustomControls
{
   public partial class ItemViewSet : UserControl, IItemViewSet
   {
      Padding FBorders;
      int FHeadersHeight;
      Color FViewsBackColor;
      Color FHeadersForeColor;
      Color FHeadersBackColor;
      Image FHeadersBackground;
      ItemViewKeyedCollection FViews;
      SplitContainer FLastSplitContainer;
      SplitContainerKeyedCollection FSplitContainers;

      public ItemViewSet()
      {
         //InitializeComponent(); // No need to create this test layout

         FHeadersHeight = 15;
         FBorders = new Padding(0);
         BackColor = Color.Transparent;
         FViews = new ItemViewKeyedCollection();
         FSplitContainers = new SplitContainerKeyedCollection();
      }

      #region IItemViewControl Properties

      public int HeadersHeight
      {
         get
         {
            return FHeadersHeight;
         }
         set
         {
            FHeadersHeight = value;

            foreach (ItemView entry in FViews) entry.HeaderHeight = value;
         }
      }
      public int AllBorderWidth
      {
         get
         {
            return Padding.Left;
         }
         set
         {
            FBorders.All = value;

            Padding = FBorders;
         }
      }
      public int LeftBorderWidth
      {
         get
         {
            return Padding.Left;
         }
         set
         {
            FBorders.Left = value;

            Padding = FBorders;
         }
      }
      public int RightBorderWidth
      {
         get
         {
            return Padding.Right;
         }
         set
         {
            FBorders.Right = value;

            Padding = FBorders;
         }
      }
      public int TopBorderWidth
      {
         get
         {
            return Padding.Top;
         }
         set
         {
            FBorders.Top = value;

            Padding = FBorders;
         }
      }
      public int BottomBorderWidth
      {
         get
         {
            return Padding.Bottom;
         }
         set
         {
            FBorders.Bottom = value;

            Padding = FBorders;
         }
      }

      public Color ViewsBackColor
      {
         get
         {
            return FViewsBackColor;
         }
         set
         {
            FViewsBackColor = value;

            foreach (ItemView entry in FViews) entry.ViewBackColor = value;
         }
      }
      public Color FlatBorderColor
      {
         get
         {
            return BackColor;
         }
         set
         {
            BackColor = value;
         }
      }
      public Color HeadersForeColor
      {
         get
         {
            return FHeadersForeColor;
         }
         set
         {
            FHeadersForeColor = value;

            foreach (ItemView entry in FViews) entry.CaptionColor = value;
         }
      }
      public Color HeadersBackColor
      {
         get
         {
            return FHeadersBackColor;
         }
         set
         {
            FHeadersBackColor = value;

            foreach (ItemView entry in FViews) entry.HeaderBackColor = value;
         }
      }
      public Image HeadersBackground
      {
         get
         {
            return FHeadersBackground;
         }
         set
         {
            FHeadersBackground = value;

            foreach (ItemView entry in FViews) entry.HeaderBackground = value;
         }
      }
      public ItemView this[string name]
      {
         get
         {
            return FViews.GetItemByKey(name);
         }
      }
      public ItemViewKeyedCollection Views
      {
         get
         {
            return FViews;
         }
      }
      public DataItem this[string viewEntry, string itemName]
      {
         get
         {
            DataItem item = null;
            ItemView entry = this[viewEntry];
            if (entry != null) item = entry[itemName];
            return item;
         }
      }

      #endregion

      void HideAllEntries(string group)
      {
         foreach (ItemView entry in FViews)
         {
            entry.Visible = string.IsNullOrEmpty(group) ? false : entry.GroupId != group;
         }
      }
      SplitContainer AddSplitContainer(string name)
      {
         SplitContainer splitContainer = new SplitContainer();

         splitContainer.Name = name;
         splitContainer.SplitterWidth = 1;
         splitContainer.Dock = DockStyle.Fill;
         splitContainer.Margin = new Padding(0);
         splitContainer.IsSplitterFixed = true;
         splitContainer.BackColor = Color.Transparent;
         splitContainer.Orientation = Orientation.Horizontal;
         splitContainer.Panel1.Padding = new Padding(0, 0, 0, 0);
         splitContainer.Panel2.Padding = new Padding(0, 0, 0, 0);
         splitContainer.Panel1.BackColor = System.Drawing.Color.Transparent;
         splitContainer.Panel2.BackColor = System.Drawing.Color.Transparent;

         if (FLastSplitContainer == null)
         {
            Controls.Add(splitContainer);
         }
         else
         {
            FLastSplitContainer.Panel2.Controls.Add(splitContainer);
            FLastSplitContainer.Panel2Collapsed = false;
         }

         splitContainer.Panel2Collapsed = true;
         FLastSplitContainer = splitContainer;
         FSplitContainers.Add(splitContainer);

         return splitContainer;
      }

      public ItemView SelectEntry(string group, string name)
      {
         HideAllEntries(group);
         ItemView entry = this[name];
         if (entry != null) entry.Visible = true;
         return entry;
      }
      public ItemView AddItemView(string name, string caption, string group, ViewStyle style)
      {
         SuspendLayout();

         SplitContainer splitContainer = FSplitContainers.GetItemByKey(group);
         if (splitContainer == null) splitContainer = AddSplitContainer(group);

         ItemView itemViewEntry = new ItemView(name, caption, group, style);
         splitContainer.Panel1.Controls.Add(itemViewEntry);
         itemViewEntry.FSplitContainer = splitContainer;
         FViews.Add(itemViewEntry);

         itemViewEntry.HeaderBackColor = HeadersBackColor;
         itemViewEntry.CaptionColor = HeadersForeColor;
         itemViewEntry.ViewBackColor = ViewsBackColor;
         itemViewEntry.HeaderHeight = HeadersHeight;

         ResumeLayout(true);

         return itemViewEntry;
      }
      public DataItem AddItem(string viewEntry, string itemName, DataItem parent, string caption, string text, string image, object data)
      {
         ItemView entry = this[viewEntry];

         return entry == null ? null : entry.AddItem(itemName, parent, caption, text, image, data);
      }
      public DataItem AddGroup(string viewEntry, string groupName, DataItem parent, string caption, string text, string image, object data, DataItem[] childs)
      {
         throw new Exception("The method or operation is not implemented.");
      }
      public DataItem AddItem(string viewEntry, string itemName, string parentName, string caption, string text, string image, object data)
      {
         ItemView entry = this[viewEntry];

         return entry == null ? null : entry.AddItem(itemName, parentName, caption, text, image, data);
      }
      public DataItem AddGroup(string viewEntry, string groupName, string parentName, string caption, string text, string image, object data, DataItem[] childs)
      {
         throw new Exception("The method or operation is not implemented.");
      }
   }

   public class ItemView : UserControl, IItemView
   {
      #region ItemView Data

      bool FAllowDrag;
      bool FAllowMove;
      bool FAllowClone;
      bool FAllowRemove;
      bool FAllowDelete;
      object FItemUnderMouse;
      string FPrimaryDropTypeName;

      string FCaption;
      string FGroupId;
      bool FIsDraggingNow;
      Control FViewControl;
      ViewStyle FViewStyle;
      Timer FFlashItemTimer;
      DataItemKeyedCollection FItems;
      Collection<DataItem> FAllItems;
      StringCollection FAllowedDropTags;
      StringCollection FAllowedDropTypes;
      Collection<DataItem> FFlashingItems;
      ListViewGroupKeyedCollection FGroups;
      SortedList<string, Image> FSmallImageList;
      SortedList<string, Image> FLargeImageList;
      internal Collection<DataItem> FNeedFullRepaintItems;

      Bitmap FPixel;
      Image FItemImage;
      Image FPlusImage;
      Image FMinusImage;
      Image FSelectedPlusImage;
      Image FSelectedMinusImage;
      Bitmap FViewControlBitmap;

      internal Panel FPanelView;
      internal Label FLabelHeader;
      internal Panel FPanelHeader;
      internal Panel FSplitButtonR;
      internal Panel FSplitButtonL;
      internal Panel FPanelHeaderLayout;
      internal SplitContainer FSplitContainer;

      Font FItemFont;
      Font FItemFontI;
      Font FItemFontU;
      Font FItemFontB;
      Font FItemFontBU;
      Font FItemExtraTextFont;

      Pen FFocusPen;
      Color FViewBackColor;
      SolidBrush FClearingBrush;
      Color FEdgesActiveColor;
      Color FEdgesInactiveColor;
      Color FLightActiveHighlightColor;
      Color FDarkActiveHighlightColor;
      Color FLightInactiveHighlightColor;
      Color FDarkInactiveHighlightColor;
      SolidBrush FTagBrush = new SolidBrush(Color.Silver);
      //SolidBrush FHotTagBrush = new SolidBrush(Color.Gray);
      SolidBrush FHotTagBrush = new SolidBrush(Color.Silver);
      SolidBrush FHotNodeBrush = new SolidBrush(Color.SlateBlue);
      SolidBrush FNodeBrush = new SolidBrush(SystemColors.ControlText);
      SolidBrush FSelectedBrush = new SolidBrush(SystemColors.Highlight);
      SolidBrush FHiddenNodeBrush = new SolidBrush(SystemColors.GrayText);

      int FNodeTextHeight;
      Rectangle FImageRect;
      Rectangle FExtImageRect;
      bool FRedrawAllowed = true;
      bool FItemsHotTrack = false;
      bool FGroupsHotTrack = false;
      bool FShowFocusRectangle = false;
      bool FUnderlineHotTracked = false;
      NodeSelectionType FNodeSelectionType = NodeSelectionType.Content;
      StringFormat FNodeStringFormat = new StringFormat(StringFormatFlags.NoWrap | StringFormatFlags.LineLimit);

      bool FIsResizing;

      int _FirstNodeNeedRedrawCount; // fixing TreeView bug with first node graphics overlapping

      #endregion

      #region ItemView Initializers

      public ItemView(string name, string caption, string group, ViewStyle style)
      {
         Name = name;
         FGroupId = group;
         Caption = caption;
         AllowDrop = false;
         FAllowMove = true;
         FAllowDrag = false;
         FAllowRemove = true;
         FAllowDelete = false;
         Dock = DockStyle.Fill;
         //Margin = new Padding(0);
         Padding = new Padding(0);
         Font = new Font("Tahoma", 8);
         ViewBackColor = Color.White;
         base.BackColor = Color.Transparent; // must be transparent to see dark borders

         FPanelView = new Panel();
         FLabelHeader = new Label();
         FSplitButtonL = new Panel();
         FSplitButtonR = new Panel();
         FPanelHeader = new Panel();
         FPanelHeaderLayout = new Panel();

         FFlashItemTimer = new System.Windows.Forms.Timer();
         FFlashItemTimer.Tick += new EventHandler(flashTimer_Tick);
         FFlashItemTimer.Interval = 700;
         FFlashItemTimer.Enabled = false;

         FItems = new DataItemKeyedCollection();
         FAllowedDropTags = new StringCollection();
         FAllowedDropTypes = new StringCollection();
         FGroups = new ListViewGroupKeyedCollection();
         FFlashingItems = new Collection<DataItem>();
         FNeedFullRepaintItems = new Collection<DataItem>();

         FPanelView.Name = "PanelView";
         FPanelView.BackColor = ViewBackColor;
         FPanelView.Padding = new Padding(0, 0, 0, 0);
         FPanelView.Dock = System.Windows.Forms.DockStyle.Fill;
         FPanelView.Paint += new PaintEventHandler(panelView_Paint);

         FPanelHeaderLayout.Name = "panelHeaderLayout";
         FPanelHeaderLayout.BackColor = Color.Transparent;
         FPanelHeaderLayout.Padding = new Padding(0, 0, 0, 1);
         FPanelHeaderLayout.Size = new System.Drawing.Size(163, 16);
         FPanelHeaderLayout.Dock = System.Windows.Forms.DockStyle.Top;
         FPanelHeaderLayout.BackgroundImageLayout = ImageLayout.Stretch;

         FSplitButtonR.Width = 15;
         FSplitButtonR.Cursor = Cursors.SizeNS;
         FSplitButtonR.Padding = new Padding(0);
         FSplitButtonR.BackColor = Color.Transparent;
         FSplitButtonR.BackgroundImageLayout = ImageLayout.Center;
         FSplitButtonR.Dock = System.Windows.Forms.DockStyle.Right;
         FSplitButtonR.BackgroundImage = Properties.Resources.SplitterGrip;
         FSplitButtonR.MouseDown += delegate(object sender, MouseEventArgs e)
         {
            FIsResizing = Parent.Parent.Parent.Parent is SplitContainer;
         };
         FSplitButtonR.MouseUp += delegate(object sender, MouseEventArgs e)
         {
            FIsResizing = false;
         };
         FSplitButtonR.MouseMove += delegate(object sender, MouseEventArgs e)
         {
            if (FIsResizing && Parent.Parent.Parent.Parent is SplitContainer)
            {
               FLabelHeader.Refresh();
               (Parent.Parent.Parent.Parent as SplitContainer).SplitterDistance =
                  Parent.Parent.Parent.Parent.PointToClient(Cursor.Position).Y - ((Control)sender).Height / 2;
            }
         };

         FSplitButtonL.Width = 15;
         FSplitButtonL.Cursor = Cursors.SizeNS;
         FSplitButtonL.Padding = new Padding(0);
         FSplitButtonL.BackColor = Color.Transparent;
         FSplitButtonL.BackgroundImageLayout = ImageLayout.Center;
         FSplitButtonL.Dock = System.Windows.Forms.DockStyle.Left;
         FSplitButtonL.BackgroundImage = Properties.Resources.SplitterGrip;
         FSplitButtonL.MouseDown += delegate(object sender, MouseEventArgs e)
         {
            FIsResizing = Parent.Parent.Parent.Parent is SplitContainer;
         };
         FSplitButtonL.MouseUp += delegate(object sender, MouseEventArgs e)
         {
            FIsResizing = false;
         };
         FSplitButtonL.MouseMove += delegate(object sender, MouseEventArgs e)
         {
            if (FIsResizing && Parent.Parent.Parent.Parent is SplitContainer)
            {
               FLabelHeader.Refresh();
               (Parent.Parent.Parent.Parent as SplitContainer).SplitterDistance =
                  Parent.Parent.Parent.Parent.PointToClient(Cursor.Position).Y - ((Control)sender).Height / 2;
            }
         };

         FPanelHeader.Name = "panelHeader";
         FPanelHeader.Padding = new Padding(0);
         FPanelHeader.Dock = System.Windows.Forms.DockStyle.Fill;
         FPanelHeader.BackgroundImageLayout = ImageLayout.Stretch;
         FPanelHeader.Paint += new PaintEventHandler(panelHeader_Paint);

         FLabelHeader.Text = caption;
         FLabelHeader.Dock = System.Windows.Forms.DockStyle.Fill;
         FLabelHeader.Margin = new System.Windows.Forms.Padding(0);
         FLabelHeader.BackColor = System.Drawing.Color.Transparent;
         FLabelHeader.TextAlign = System.Drawing.ContentAlignment.TopCenter;
         FLabelHeader.ImageAlign = System.Drawing.ContentAlignment.MiddleRight;
         //FLabelHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(204)));
         FLabelHeader.MouseClick += (s, e) => { if (HeaderMouseClick != null) HeaderMouseClick(this, e); };

         Controls.Add(FPanelView);
         Controls.Add(FPanelHeaderLayout);
         FPanelHeader.Controls.Add(FSplitButtonL);
         FPanelHeader.Controls.Add(FSplitButtonR);
         FPanelHeader.Controls.Add(FLabelHeader);
         FPanelHeaderLayout.Controls.Add(FPanelHeader);

         ViewStyle = style;

         FSmallImageList = new SortedList<string, Image>();

         FItemImage = Properties.Resources.ThumbnailLoadingHS;
         ((Bitmap)FItemImage).MakeTransparent(Color.Magenta);

         FPlusImage = Properties.Resources.plus_miranda;
         ((Bitmap)FPlusImage).MakeTransparent(Color.Magenta);

         FMinusImage = Properties.Resources.minus_miranda;
         ((Bitmap)FMinusImage).MakeTransparent(Color.Magenta);

         FSelectedPlusImage = Properties.Resources.plus_miranda_Selected;
         ((Bitmap)FSelectedPlusImage).MakeTransparent(Color.Magenta);

         FSelectedMinusImage = Properties.Resources.minus_miranda_Selected;
         ((Bitmap)FSelectedMinusImage).MakeTransparent(Color.Magenta);

         FNodeStringFormat.Trimming = StringTrimming.EllipsisCharacter;
         FNodeTextHeight = TextRenderer.MeasureText("A", FItemFont).Height;

         FDarkActiveHighlightColor = Color.FromArgb(50, 150, 255);

         HSLColor hsl = new HSLColor(FDarkActiveHighlightColor);

         hsl.Luminosity += 25;
         FLightActiveHighlightColor = (Color)hsl;

         hsl.Luminosity += 25;
         FEdgesActiveColor = (Color)hsl;

         hsl.Luminosity += 5;
         FDarkInactiveHighlightColor = (Color)hsl;

         hsl.Luminosity += 5;
         FLightInactiveHighlightColor = (Color)hsl;

         hsl.Luminosity += 5;
         FEdgesInactiveColor = (Color)hsl;

         FPixel = new Bitmap(1, 1);
      }

      ~ItemView()
      {
         FFlashItemTimer.Dispose();
      }

      #endregion

      #region ItemView Properties

      public new int Height
      {
         get
         {
            if (FSplitContainer != null)
            {
               return FSplitContainer.SplitterDistance;
            }
            else
            {
               return ((Control)this).Height;
            }
         }
         set
         {
            if (FSplitContainer != null)
            {
               FSplitContainer.SplitterDistance = value;
            }
            else
            {
               ((Control)this).Height = value;
            }
         }
      }
      public string Caption
      {
         get
         {
            return FCaption;
         }
         set
         {
            FCaption = value;
            if (FLabelHeader != null) FLabelHeader.Text = FCaption;
         }
      }
      public string GroupId
      {
         get
         {
            return FGroupId;
         }
      }
      public bool AllowDrag
      {
         get
         {
            return FAllowDrag;
         }
         set
         {
            FAllowDrag = value;
            AllowDrop = value;
            FViewControl.AllowDrop = value;
         }
      }
      public bool AllowMove
      {
         get
         {
            return FAllowMove;
         }
         set
         {
            FAllowMove = value;
         }
      }
      public bool AllowClone
      {
         get
         {
            return FAllowClone;
         }
         set
         {
            FAllowClone = value;
         }
      }
      public bool AllowRemove
      {
         get
         {
            return FAllowRemove;
         }
         set
         {
            FAllowRemove = value;
         }
      }
      public bool AllowDelete
      {
         get
         {
            return FAllowDelete;
         }
         set
         {
            FAllowDelete = value;
         }
      }
      public int HeaderHeight
      {
         get
         {
            return FPanelHeaderLayout.Height;
         }
         set
         {
            FPanelHeaderLayout.Height = value;
         }
      }
      public new Padding Margin
      {
         get
         {
            return FPanelView.Padding;
         }
         set
         {
            FPanelView.Padding = value;
         }
      }
      public override Font Font
      {
         get
         {
            return base.Font;
         }
         set
         {
            base.Font = value;
            FItemFont = new Font(value.FontFamily, value.Size, FontStyle.Regular);
            FItemFontI = new Font(value.FontFamily, value.Size, FontStyle.Italic);
            FItemFontU = new Font(value.FontFamily, value.Size, FontStyle.Underline);
            FItemFontB = new Font(value.FontFamily, value.Size, FontStyle.Bold);
            FItemExtraTextFont = new Font("Arial", value.Size - 0, FontStyle.Italic);
            FItemFontBU = new Font(value.FontFamily, value.Size, FontStyle.Bold | FontStyle.Underline);
         }
      }
      public bool ShowSplitters
      {
         get
         {
            return FSplitButtonR.Visible;
         }
         set
         {
            FSplitButtonR.Visible = value;
            FSplitButtonL.Visible = value;
         }
      }
      public Color CaptionColor
      {
         get
         {
            return FLabelHeader.ForeColor;
         }
         set
         {
            FLabelHeader.ForeColor = value;
         }
      }
      public Color ViewBackColor
      {
         get
         {
            return FViewBackColor;
         }
         set
         {
            FViewBackColor = value;
            if (FPanelView != null) FPanelView.BackColor = value;
            if (FViewControl != null) FViewControl.BackColor = value;
            if (FClearingBrush != null) FClearingBrush.Color = value;
         }
      }
      public bool ShowCheckBoxes
      {
         get
         {
            return ((TreeView)FViewControl).CheckBoxes;
         }
         set
         {
            ResetDefaultImages();
            ((TreeView)FViewControl).CheckBoxes = value;
         }
      }
      public Control ViewControl
      {
         get
         {
            return FViewControl;
         }
      }
      public ViewStyle ViewStyle
      {
         get
         {
            return FViewStyle;
         }
         set
         {
            if (value == FViewStyle || value == ViewStyle.None) return;

            if (value == ViewStyle.Tree && (FViewStyle != ViewStyle.Tree || FViewStyle == ViewStyle.None))
            {
               CreateTreeView();
            }
            else if (value != ViewStyle.Tree && (FViewStyle == ViewStyle.Tree || FViewStyle == ViewStyle.None))
            {
               CreateListView();
            }

            switch (value)
            {
               case ViewStyle.List:
               {
                  ((ListView)FViewControl).View = View.List;
                  break;
               }
               case ViewStyle.Icons:
               {
                  ((ListView)FViewControl).View = View.SmallIcon;
                  break;
               }
               case ViewStyle.Details:
               {
                  ((ListView)FViewControl).View = View.Details;
                  break;
               }
               case ViewStyle.Thumbnails:
               {
                  ((ListView)FViewControl).View = View.LargeIcon;
                  break;
               }
            }

            FViewStyle = value;
         }
      }
      public Color HeaderBackColor
      {
         get
         {
            return FPanelHeader.BackColor;
         }
         set
         {
            FPanelHeader.BackColor = value;
         }
      }
      public Image HeaderBackground
      {
         get
         {
            return FPanelHeader.BackgroundImage;
         }
         set
         {
            FPanelHeader.BackgroundImage = value;
         }
      }
      public DataItem SelectedItem
      {
         get
         {
            TreeNode item = ((TreeView)FViewControl).SelectedNode;

            return item == null ? null : (DataItem)item.Tag;
         }
         set
         {
            ((TreeView)FViewControl).SelectedNode = value == null ? null : value.UiElement as TreeNode;
         }
      }
      public bool ShowFocusRectangle
      {
         get
         {
            return FShowFocusRectangle;
         }
         set
         {
            FShowFocusRectangle = value;

            Refresh(SelectedItem);
         }
      }
      public string PrimaryDropTypeName
      {
         get
         {
            return FPrimaryDropTypeName;
         }
         set
         {
            FPrimaryDropTypeName = value;
         }
      }
      public DataItem this[string name]
      {
         get
         {
            return FItems.GetItemByKey(name != null ? name : "");
         }
      }
      public Collection<DataItem> Items
      {
         get
         {
            return (Collection<DataItem>)FItems;
         }
      }
      public Collection<DataItem> AllItems
      {
         get
         {
            if (FAllItems == null) FAllItems = new Collection<DataItem>();

            FAllItems.Clear();

            TreeNodeCollection nodes = ((TreeView)FViewControl).Nodes;

            foreach (TreeNode n in nodes) IterateNodes(n);

            return FAllItems;
         }
      }
      public StringCollection AllowedDropTags
      {
         get
         {
            return FAllowedDropTags;
         }
      }
      public StringCollection AllowedDropTypes
      {
         get
         {
            return FAllowedDropTypes;
         }
      }
      public NodeSelectionType NodeSelectionType
      {
         get
         {
            return FNodeSelectionType;
         }
         set
         {
            FNodeSelectionType = value;
            FViewControl.Refresh();
         }
      }
      public SortedList<string, Image> SmallImageList
      {
         get
         {
            return FSmallImageList;
         }
         set
         {
            if (FSmallImageList == value) return;

            FSmallImageList = value;

            if (FViewControl == null) ViewStyle = ViewStyle.Tree; else FViewControl.Refresh();
         }
      }
      public SortedList<string, Image> LargeImageList
      {
         get
         {
            return FLargeImageList;
         }
         set
         {
         }
      }

      #endregion

      #region IItemView Events

      public event MouseEventHandler HeaderMouseClick;
      public event EventHandler<PaintEventArgs> HeaderPaint;
      public event EventHandler<ItemViewBasicEventArgs> ItemHover;
      public event EventHandler<ItemViewBasicEventArgs> ItemAdded;
      public event EventHandler<ItemViewMovedEventArgs> ItemMoved;
      public event EventHandler<ItemViewCancelEventArgs> ItemAdding;
      public event EventHandler<ItemViewMovingEventArgs> ItemMoving;
      public event EventHandler<ItemViewBasicEventArgs> ItemRemoved;
      public event EventHandler<ItemsCompareEventArgs> ItemsCompare;
      public event EventHandler<ItemViewBasicEventArgs> ItemSelected;
      public event EventHandler<ItemViewBasicEventArgs> ItemActivated;
      public event EventHandler<ItemViewBasicEventArgs> ItemDeselected;
      public event EventHandler<ItemViewBasicEventArgs> ItemContextMenu;
      public event EventHandler<ItemViewRemovingEventArgs> ItemRemoving;
      public event EventHandler<ItemViewDragDropEventArgs> ItemDragging;
      public event EventHandler<ItemViewDragDropEventArgs> ItemDropping;
      public event EventHandler<ItemViewXmlAssignEventArgs> XmlAssign;
      public event EventHandler<ItemViewXmlRetrieveEventArgs> XmlRetrieve;
      public event EventHandler<ItemViewExpandedCollapsedEventArgs> ItemExpanded;
      public event EventHandler<ItemViewExpandedCollapsedEventArgs> ItemCollapsed;

      #endregion

      #region ItemView Members

      private void CreateTreeView()
      {
         SuspendLayout();

         if (FViewControl != null) FViewControl.Dispose();

         TreeView treeView = new TreeView();

         FViewControl = treeView;

         treeView.Indent = 15;
         treeView.TabIndex = 0;
         treeView.ItemHeight = 18;
         treeView.AllowDrop = false;
         //treeView.ShowLines = false;
         //treeView.Scrollable = false;
         treeView.CheckBoxes = false;
         treeView.FullRowSelect = true;
         //treeView.ShowPlusMinus = false;
         //treeView.ShowRootLines = false;
         treeView.HideSelection = false;
         treeView.Dock = DockStyle.Fill;
         treeView.Margin = new Padding(0);
         treeView.Padding = new Padding(0);
         treeView.Name = Name + "TreeView";
         treeView.HotTracking = FItemsHotTrack;
         treeView.BorderStyle = BorderStyle.None;
         treeView.BackColor = FPanelView.BackColor;
         treeView.TreeViewNodeSorter = new NodeSorter();
         treeView.DrawMode = TreeViewDrawMode.OwnerDrawAll;
         treeView.Font = new Font("Tahoma", 8.25F, FontStyle.Regular);
         
         treeView.Click += new EventHandler(treeView_Click);
         treeView.KeyDown += new KeyEventHandler(treeView_KeyDown);
         treeView.DragDrop += new DragEventHandler(itemView_DragDrop);
         treeView.DragOver += new DragEventHandler(itemView_DragOver);
         treeView.MouseUp += new MouseEventHandler(treeView_MouseUp);
         treeView.MouseDown += new MouseEventHandler(treeView_MouseDown);
         treeView.MouseMove += new MouseEventHandler(treeView_MouseMove);
         treeView.ItemDrag += new ItemDragEventHandler(itemView_ItemDrag);
         treeView.DoubleClick += new EventHandler(treeView_DoubleClick);
         treeView.DrawNode += new DrawTreeNodeEventHandler(treeView_DrawNode);
         treeView.AfterSelect += new TreeViewEventHandler(treeView_AfterSelect);
         treeView.AfterExpand += new TreeViewEventHandler(treeView_AfterExpand);
         //treeView.AfterCollapse += new TreeViewEventHandler(treeView_AfterExpandCollapse);
         treeView.MouseDoubleClick += new MouseEventHandler(treeView_MouseDoubleClick);
         treeView.BeforeSelect += new TreeViewCancelEventHandler(treeView_BeforeSelect);
         treeView.AfterLabelEdit += new NodeLabelEditEventHandler(treeView_AfterLabelEdit);
         treeView.NodeMouseHover += new TreeNodeMouseHoverEventHandler(treeView_NodeMouseHover);
         treeView.NodeMouseDoubleClick += new TreeNodeMouseClickEventHandler(treeView_NodeMouseDoubleClick);
         treeView.ChangeUICues += (o, e) => { ShowFocusRectangle = e.ShowFocus; };

         FClearingBrush = new SolidBrush(BackColor);
         FImageRect = new Rectangle(0, 0, treeView.ItemHeight, treeView.ItemHeight);
         FExtImageRect = new Rectangle(0, 0, treeView.ItemHeight, treeView.ItemHeight);

         FPanelView.Controls.Add(treeView);

         ResumeLayout(true);
      }
      private void CreateListView()
      {
         SuspendLayout();

         if (FViewControl != null) FViewControl.Dispose();

         ListView listView = new ListView();
         FViewControl = (ListView)listView;

         listView.View = View.List;
         listView.AllowDrop = false;
         listView.Dock = DockStyle.Fill;
         listView.Margin = new Padding(0);
         listView.Padding = new Padding(0);
         listView.Name = Name + "ListView";
         listView.BackColor = Color.WhiteSmoke;
         listView.BorderStyle = BorderStyle.None;
         listView.UseCompatibleStateImageBehavior = false;
         listView.DragDrop += new DragEventHandler(itemView_DragDrop);
         listView.DragOver += new DragEventHandler(itemView_DragOver);
         listView.ItemDrag += new ItemDragEventHandler(itemView_ItemDrag);
         listView.ItemActivate += new EventHandler(treeView_DoubleClick);

         FPanelView.Controls.Add(listView);

         ResumeLayout(true);
      }
      private TreeView GetTreeView()
      {
         return FViewControl as TreeView;
      }
      private void ResetDefaultImages()
      {
         ((TreeView)FViewControl).StateImageList.Images.Clear();
      }
      private void IterateNodes(TreeNode treeNode)
      {
         FAllItems.Add((DataItem)treeNode.Tag);

         foreach (TreeNode n in treeNode.Nodes) IterateNodes(n);
      }
      private void FireSelectedEvent(TreeNode node)
      {
         if (node.Tag != null && ItemSelected != null) ItemSelected(this, new ItemViewBasicEventArgs((DataItem)node.Tag));
      }
      private void FireDeselectedEvent(TreeNode node)
      {
         if (node.Tag != null && ItemDeselected != null) ItemDeselected(this, new ItemViewBasicEventArgs((DataItem)node.Tag));
      }
      private Rectangle EvalNodeBounds(TreeNode node)
      {
         Rectangle nodeRect = node.Bounds;

         DataItem item = node.Tag as DataItem;

         nodeRect.Width = (int)TextRenderer.MeasureText(item.Text, item.IsGroup ? FItemFontB : FItemFont).Width + 0;

         if (FNodeSelectionType == NodeSelectionType.Content && !string.IsNullOrEmpty(item.Comment))
         {
            nodeRect.Width += (int)TextRenderer.MeasureText(item.Comment, FItemExtraTextFont).Width + 3;
         }

         if (nodeRect.X + nodeRect.Width >= FViewControl.Width) nodeRect.Width = FViewControl.Width - nodeRect.X;

         return nodeRect;
      }
      private object GetItemAt(object sender, int x, int y)
      {
         TreeNode node = null;
         ListViewItem item = null;
         TreeView treeView = null;
         ListView listView = null;

         if (sender.GetType() == typeof(TreeView))
         {
            treeView = (TreeView)sender;
            node = treeView.GetNodeAt(x, y);
         }
         else
         {
            listView = (ListView)sender;
            item = listView.GetItemAt(x, y);
         }

         return (node == null ? (object)item : (object)node);
      }
      private void DrawEdges(Graphics g, Rectangle r)
      {
         Color color = FViewControl.Focused ? FEdgesActiveColor : FEdgesInactiveColor;

         FPixel.SetPixel(0, 0, FViewControl.BackColor);

         g.DrawImageUnscaled(FPixel, r.X, r.Y);
         g.DrawImageUnscaled(FPixel, r.X + r.Width - 1, r.Y);
         g.DrawImageUnscaled(FPixel, r.X, r.Y + r.Height - 1);
         g.DrawImageUnscaled(FPixel, r.X + r.Width - 1, r.Y + r.Height - 1);

         FPixel.SetPixel(0, 0, color);

         g.DrawImageUnscaled(FPixel, r.X + 1, r.Y);
         g.DrawImageUnscaled(FPixel, r.X + r.Width - 2, r.Y);
         g.DrawImageUnscaled(FPixel, r.X + 1, r.Y + r.Height - 1);
         g.DrawImageUnscaled(FPixel, r.X + r.Width - 2, r.Y + r.Height - 1);

         g.DrawImageUnscaled(FPixel, r.X, r.Y + 1);
         g.DrawImageUnscaled(FPixel, r.X + r.Width - 1, r.Y + 1);
         g.DrawImageUnscaled(FPixel, r.X, r.Y + r.Height - 2);
         g.DrawImageUnscaled(FPixel, r.X + r.Width - 1, r.Y + r.Height - 2);
      }
      private bool ContainsNode(TreeNode node, TreeNode targetNode)
      {
         if (targetNode == null) return false;
         if (targetNode.Parent == null) return false;
         if (targetNode.Parent.Equals(node)) return true;

         return ContainsNode(node, targetNode.Parent);
      }
      private DragDropInfo ValidateDroppingData(IDataObject dropObject)
      {
         if (!dropObject.GetDataPresent(typeof(DragDropInfo))) return null;

         DragDropInfo info = (DragDropInfo)dropObject.GetData(typeof(DragDropInfo));

         if (info.SourceControl == this) return info;

         bool allowed = false;

         foreach (string tagTarget in FAllowedDropTags)
         {
            foreach (string tagSource in ((IItemView)info.SourceControl).AllowedDropTags)
            {
               if (tagTarget == tagSource) { allowed = true; break; }
            }
            if (allowed) break;
         }

         if (!allowed) return null;

         foreach (string typeName in FAllowedDropTypes) if (info.DraggingItem[typeName] != null) return info;

         return FAllowedDropTypes.Count == 0 ? info : null;
      }
      internal int FireItemsCompareEvent(DataItem item1, DataItem item2)
      {
         ItemsCompareEventArgs args = new ItemsCompareEventArgs() { Item1 = item1, Item2 = item2 };

         if (ItemsCompare != null) ItemsCompare(this, args);

         return args.CompareResult;
      }
      private void FireExpandedCollapsedEvent(TreeNode node, bool byUser)
      {
         DataItem item = (DataItem)node.Tag;

         if (node.IsExpanded)
         {
            if (node.Tag != null && ItemExpanded != null) ItemExpanded(this, new ItemViewExpandedCollapsedEventArgs(item, byUser));
         }
         else
         {
            if (node.Tag != null && ItemCollapsed != null) ItemCollapsed(this, new ItemViewExpandedCollapsedEventArgs(item, byUser));
         }
      }
      static internal TreeNode GetNextNode(TreeNode srcNode, int maxLevel)
      {
         TreeNode nextNode = srcNode.NextVisibleNode;

         return nextNode != null ? (maxLevel >= 0 && nextNode.Level > maxLevel ? nextNode : null) : null;
      }
      private HitTestRegion GetHitTestRegion(Rectangle bounds, int level, Point point)
      {
         int x = point.X, y = point.Y;

         Rectangle leftImageRect = bounds;
         leftImageRect.Width = FImageRect.Width;
         leftImageRect.X = level * FImageRect.Width;

         Rectangle commentRect = bounds;
         commentRect.X = bounds.X + bounds.Width;
         int w = FViewControl.Width - commentRect.X - FImageRect.Width;
         commentRect.Width = w > 0 ? w : 1;

         Rectangle rightImageRect = bounds;
         rightImageRect.Width = FImageRect.Width;
         rightImageRect.X = commentRect.X + commentRect.Width;

         if (leftImageRect.Contains(x, y)) return HitTestRegion.LeftImage;
         else if (bounds.Contains(x, y)) return HitTestRegion.Text;
         else if (commentRect.Contains(x, y)) return HitTestRegion.Comment;
         else if (rightImageRect.Contains(x, y)) return HitTestRegion.RightImage;
         else return HitTestRegion.None;
      }
      private DataItem AddItem(DataItem item, DataItem parent, DropResults dropResult)
      {
         if (item.UiElement == null)
         {
            TreeNode node = new TreeNode(item.Text);

            node.Tag = item;
            node.Name = item.Name;

            item.UiElement = node;
         }

         TreeNode curNode = (TreeNode)item.UiElement;

         int level = curNode.Level;

         for (; ; )
         {
            DataItem curItem = (DataItem)curNode.Tag;

            ItemViewCancelEventArgs args = null;

            if (ItemAdding != null) ItemAdding(this, args = new ItemViewCancelEventArgs(curItem));

            if (args != null && args.Cancel) return null;

            curItem.View = this;

            if (FItems.Contains(curItem.Name))
            {
               curItem.Original = FItems[curItem.Name];
               curItem.Original.Duplicates.Add(curItem);
               //curItem.Text += "'";
            }
            else
            {
               FItems.Add(curItem);
            }

            if (ItemAdded != null) ItemAdded(this, new ItemViewBasicEventArgs(curItem));

            curNode = GetNextNode(curNode, level);

            if (curNode == null) break;
         }

         InsertNode((TreeNode)item.UiElement, (parent == null ? null : (TreeNode)parent.UiElement), dropResult);

         return item;
      }
      private void InsertNode(TreeNode newNode, TreeNode trgNode, DropResults dropResult)
      {
         TreeView treeView = (TreeView)FViewControl;

         if (newNode.TreeView == treeView) newNode.Remove();

         DropResults _dropResult = dropResult != DropResults.Auto ? dropResult :
            ((trgNode.Tag as DataItem).IsGroup ? DropResults.AppendChild : DropResults.InsertBefore);
         
         if ((_dropResult & DropResults.PrependChild) == DropResults.PrependChild)
         {
            (trgNode == null ? treeView.Nodes : trgNode.Nodes).Insert(0, newNode);
         }
         else if ((_dropResult & DropResults.AppendChild) == DropResults.AppendChild)
         {
            (trgNode == null ? treeView.Nodes : trgNode.Nodes).Add(newNode);

            // In multiple adding this makes view to look ugly
            //if (trgNode != null) trgNode.Expand();
         }
         else if ((_dropResult & DropResults.InsertBefore) == DropResults.InsertBefore)
         {
            (trgNode == null ? treeView.Nodes : (trgNode.Parent == null ? treeView.Nodes : trgNode.Parent.Nodes)).Insert((trgNode == null ? 0 : trgNode.Index), newNode);
         }
         else if ((_dropResult & DropResults.InsertAfter) == DropResults.InsertAfter)
         {
            (trgNode == null ? treeView.Nodes : (trgNode.Parent == null ? treeView.Nodes : trgNode.Parent.Nodes)).Insert((trgNode == null ? treeView.Nodes.Count : trgNode.Index + 1), newNode);
         }

         // In multiple adding this makes view to look ugly
         //treeView.SelectedNode = newNode;

         _FirstNodeNeedRedrawCount++;

         if (FAllItems != null) FAllItems.Clear();
      }
      private bool ValidateNodesForDragDropOperation(TreeNode draggedNode, TreeNode targetNode)
      {
         return (!draggedNode.Equals(targetNode) && !ContainsNode(draggedNode, targetNode));
      }
      
      public void Sort()
      {
         TreeView tv = GetTreeView();

         if (tv != null)
         {
            HideView();
            tv.BeginUpdate();

            try
            {
               tv.Sort();
            }
            catch
            {
               //
            }

            tv.EndUpdate();
            ShowView();
         }
      }
      public void Clear()
      {
         FItems.Clear();
         FGroups.Clear();
         if (FAllItems != null) FAllItems.Clear();

         if (ViewStyle == ViewStyle.Tree)
         {
            ((TreeView)FViewControl).SelectedNode = null;
            ((TreeView)FViewControl).Nodes.Clear();
         }
         else
         {
            ((ListView)FViewControl).Items.Clear();
         }
      }
      public void HideView()
      {
         if (FViewControl != null && FViewControl.Visible)
         {
            //HACK: The cost of this hack for each call > 1Mb
            if (FViewControlBitmap != null) FViewControlBitmap.Dispose();
            //Rectangle r = FViewControl.Bounds;
            //FViewControlBitmap = new Bitmap(r.Width, r.Height);
            //FViewControl.DrawToBitmap(FViewControlBitmap, r);
            FViewControl.Hide();
         }
         FPanelView.Refresh();
         //Application.DoEvents();
      }
      public void ShowView()
      {
         if (FViewControl != null) FViewControl.Show();
      }
      public void EndUpdate()
      {
         TreeView tv = GetTreeView();

         if (tv != null)
         {
            tv.EndUpdate();
            FRedrawAllowed = true;
         }
      }
      public void BeginUpdate()
      {
         TreeView tv = GetTreeView();

         if (tv != null)
         {
            tv.BeginUpdate();
            FRedrawAllowed = false;
         }
      }
      public XElement GetXStructure()
      {
         if (XmlRetrieve == null) throw new Exception("To save data to xml you must handle XmlRetrieve event.");

         XElement xml = new XElement("Root");

         ICollection<DataItem> items = AllItems;

         if (items.Count > 0)
         {
            XElement xParent = null;

            ItemViewXmlRetrieveEventArgs args = new ItemViewXmlRetrieveEventArgs();

            Dictionary<DataItem, XElement> xmlWritingMap = new Dictionary<DataItem, XElement>(100);

            foreach (DataItem item in items)
            {
               if (item.Parent != null) xmlWritingMap.TryGetValue(item.Parent, out xParent); else xParent = null;

               args.Item = item;
               args.XElement = null;
               args.SkipItem = false;
               args.Expanded = false;
               args.XName = item.IsGroup ? "Group" : "";

               XmlRetrieve(this, args);

               if (args.SkipItem) continue;

               if (args.XElement == null)
               {
                  if (string.IsNullOrEmpty(args.XName)) throw new Exception("Node in the xml must have a name.");
                  if (string.IsNullOrEmpty(args.Name) || string.IsNullOrEmpty(args.NameAttrId)) throw new Exception("Item in the view must have an identifier.");
                  if (string.IsNullOrEmpty(args.Caption) || string.IsNullOrEmpty(args.CaptionAttrId)) throw new Exception("Item in the view must have a caption.");

                  args.XElement = new XElement(args.XName);
                  args.XElement.SetAttributeValue(args.NameAttrId, args.Name);
                  args.XElement.SetAttributeValue(args.CaptionAttrId, args.Caption);
                  //args.XElement.SetAttributeValue("Expanded", args.Expanded);
                  if (args.Expanded) args.XElement.SetAttributeValue("Expanded", true);
               }

               if (xParent == null) xml.Add(args.XElement); else xParent.Add(args.XElement);

               xmlWritingMap.Add(item, args.XElement);
            }
            xmlWritingMap.Clear();
            items.Clear();
         }
         return xml;
      }
      public void Refresh(DataItem item)
      {
         if (item != null && FViewControl != null && FViewControl.Visible)
         {
            if (item.UiElement is TreeNode)
            {
               Rectangle r = (item.UiElement as TreeNode).Bounds;
               r.X = 0;
               r.Width = FViewControl.Width;
               FViewControl.Invalidate(r);
               if (item.FBeginUpdateCount == 0) FViewControl.Update();
            }
         }
      }
      public void EndUpdate(DataItem item)
      {
         item.FBeginUpdateCount--;

         if (item.FBeginUpdateCount < 0)
         {
            item.FBeginUpdateCount = 0;
         }
         else if (item.FBeginUpdateCount == 0)
         {
            if (FViewControl != null && FViewControl.Visible) FViewControl.Update();
         }
      }
      public void BeginUpdate(DataItem item)
      {
         item.FBeginUpdateCount++;
      }
      public void CopyFrom(IItemView source)
      {
         Clear();

         foreach (TreeNode node in ((TreeView)source.ViewControl).Nodes)
         {
            AddItem(((DataItem)node.Tag).Duplicate(true), null, DropResults.AppendChild);
         }

         ((TreeView)FViewControl).ExpandAll();
      }
      public void RemoveItem(DataItem item)
      {
         ItemViewRemovingEventArgs args = null;

         if (ItemRemoving != null) ItemRemoving(this, args = new ItemViewRemovingEventArgs(item));

         if (args != null && args.Cancel) return;

         RemoveItem(item, args == null ? true : args.WithChildren, args == null ? null : args.NewChildrenParent);
      }
      public void StopFlashItem(DataItem item)
      {
         for (int i = 0; i < item.Duplicates.Count + 1; i++)
         {
            DataItem curItem = (DataItem)(i == 0 ? item : item.Duplicates[i - 1]);
            FFlashingItems.Remove(curItem);
            curItem.FCurrentImageIndex = 0;
            //curItem.ImageName = curItem.FDefaultImageName;
            if (FFlashingItems.Count == 0)
            {
               FFlashItemTimer.Stop();
               curItem.FAnimationImages.Clear();
            }
            FViewControl.Refresh();
         }
      }
      public void Expand(IEnumerable<string> names)
      {
         if (names == null || names.Count() == 0)
         {
            GetTreeView().ExpandAll();
         }
         else
         {
            foreach (string name in names)
            {
               DataItem item = this[name];

               if (item != null && item.UiElement != null) ((TreeNode)item.UiElement).Expand();
            }
         }
      }
      public void Collapse(IEnumerable<string> names)
      {
         if (names == null || names.Count() == 0)
         {
            GetTreeView().CollapseAll();
         }
         else
         {
            foreach (string name in names)
            {
               DataItem item = this[name];

               if (item != null && item.UiElement != null) ((TreeNode)item.UiElement).Collapse();
            }
         }
      }
      public void RenameItem(DataItem item, string newName)
      {
         FItems.RenameItem(item, newName);
         (item as DataItem).Name = newName;
      }
      public bool SetXStructure(XElement xml, object context)
      {
         if (XmlAssign == null) throw new Exception("To load data from xml you must handle XmlAssign event.");

         if (xml == null || !xml.HasElements) return false;

         DataItem parent = null, item = null;

         IEnumerable<XElement> xElements = xml.Descendants();

         ItemViewXmlAssignEventArgs args = new ItemViewXmlAssignEventArgs();

         Dictionary<XElement, DataItem> xmlReadingMap = new Dictionary<XElement, DataItem>(100);

         try
         {
            try
            {
               BeginUpdate();

               Clear();

               foreach (XElement xEl in xElements)
               {
                  xmlReadingMap.TryGetValue(xEl.Parent, out parent);

                  args.Name = null;
                  args.Text = null;
                  args.Data = null;
                  args.ImageName = "";
                  args.XElement = xEl;
                  args.Caption = null;
                  args.Hidden = false;
                  args.IsGroup = false;
                  args.SkipItem = false;
                  args.ExtImageName = "";
                  args.Context = context;
                  args.ParentItem = parent;

                  if (xEl.Name == "Group") args.IsGroup = true;

                  XmlAssign(this, args);

                  if (args.SkipItem) continue;

                  if (string.IsNullOrEmpty(args.NameAttrId)) args.NameAttrId = "Name";
                  if (string.IsNullOrEmpty(args.CaptionAttrId)) args.CaptionAttrId = "Caption";

                  if (string.IsNullOrEmpty(args.Name)) args.Name = (string)xEl.Attribute(args.NameAttrId);
                  if (string.IsNullOrEmpty(args.Caption)) args.Caption = (string)xEl.Attribute(args.CaptionAttrId);

                  if (string.IsNullOrEmpty(args.Caption)) args.Caption = args.Name;

                  if (string.IsNullOrEmpty(args.Name)) throw new Exception("Item in the view must have an identifier.");
                  if (string.IsNullOrEmpty(args.Caption)) throw new Exception("Item in the view must have a caption.");

                  item = args.IsGroup ? AddGroup(args.Name, parent, args.Caption, args.Text, args.ImageName, args.Data) :
                                        AddItem(args.Name, parent, args.Caption, args.Text, args.ImageName, args.Data);

                  item.Hidden = args.Hidden;
                  item.ExtImageName = args.ExtImageName;

                  xmlReadingMap.Add(xEl, item);
               }

               xmlReadingMap.Clear();

            }
            finally
            {
               ((TreeView)FViewControl).CollapseAll();

               EndUpdate();
            }
         }
         catch
         {
            Clear(); throw;
         }

         return true;
      }
      public DataItem AddItem(DataItem item, DataItem parent)
      {
         return AddItem(item, parent, DropResults.AppendChild);
      }
      public void AddHeaderButton(Image image, EventHandler click, bool right)
      {
         Panel button = new Panel();

         button.Width = 15;
         button.Padding = new Padding(0);
         button.BackgroundImage = image;
         button.BackColor = Color.Transparent;
         button.BackgroundImageLayout = ImageLayout.Center;
         button.Dock = right ? System.Windows.Forms.DockStyle.Right : DockStyle.Left;
         button.Click += click;

         FPanelHeader.Controls.Add(button);
      }
      public void MoveItem(DataItem item, DataItem parent, DropResults dropResult)
      {
         DataItem oldParent = item.Parent;

         if (item.View == this)
         {
            InsertNode((TreeNode)item.UiElement, (parent == null ? null : (TreeNode)parent.UiElement), dropResult);
         }
         else
         {
            if (CopyItem(item, parent, dropResult) != null && !FIsDraggingNow)
            {
               if (item.View.AllowRemove) item.View.RemoveItem(item);
            }
         }
         if (ItemMoved != null) ItemMoved(this, new ItemViewMovedEventArgs(item, oldParent));
      }
      public void RemoveItem(DataItem item, bool withChildren, DataItem newParent)
      {
         TreeNode currentNode = (TreeNode)item.UiElement;
         int level = currentNode.Level;

         if (withChildren)
         {
            for (; ; )
            {
               DataItem currentItem = (DataItem)currentNode.Tag;

               if (currentItem.IsOriginal)
               {
                  foreach (DataItem dup in currentItem.Duplicates)
                  {
                     ((TreeNode)dup.UiElement).Remove();
                     if (ItemRemoved != null) ItemRemoved(this, new ItemViewBasicEventArgs(dup));
                  }

                  currentItem.Duplicates.Clear();

                  FItems.Remove(currentItem);
               }
               else
               {
                  currentItem.Original.Duplicates.Remove(currentItem);
               }

               if (ItemRemoved != null) ItemRemoved(this, new ItemViewBasicEventArgs(currentItem));

               currentNode = GetNextNode(currentNode, level);

               if (currentNode == null) break;
            }
         }
         else
         {
            DataItem currentItem = (DataItem)currentNode.Tag;

            if (currentItem.IsOriginal)
            {
               foreach (DataItem dup in currentItem.Duplicates)
               {
                  ((TreeNode)dup.UiElement).Remove();
                  if (ItemRemoved != null) ItemRemoved(this, new ItemViewBasicEventArgs(dup));
               }

               currentItem.Duplicates.Clear();

               FItems.Remove(currentItem);
            }
            else
            {
               currentItem.Original.Duplicates.Remove(currentItem);
            }

            if (ItemRemoved != null) ItemRemoved(this, new ItemViewBasicEventArgs(currentItem));

            TreeNode[] nodes = new TreeNode[currentNode.Nodes.Count];
            currentNode.Nodes.CopyTo(nodes, 0);
            foreach (TreeNode node in nodes) MoveItem(node.Tag as DataItem, newParent, DropResults.AppendChild);
         }

         ((TreeNode)item.UiElement).Remove();

         if (FAllItems != null) FAllItems.Clear();
      }
      public void FlashItem(DataItem item, string image1, string image2, int period)
      {
         DataItem curItem = null;
         
         for (int i = 0; i < item.Duplicates.Count + 1; i++)
         {
            curItem = (DataItem)(i == 0 ? item : item.Duplicates[i - 1]);

            curItem.FCurrentImageIndex = 0;
            curItem.FAnimationImages.Clear();
            if (!FFlashingItems.Contains(curItem)) FFlashingItems.Add(curItem);
            if (!curItem.FAnimationImages.Contains(image1)) curItem.FAnimationImages.Add(image1);
            if (!curItem.FAnimationImages.Contains(image2)) curItem.FAnimationImages.Add(image2);
         }

         FFlashItemTimer.Interval = period;
         FFlashItemTimer.Start();
      }
      public DataItem CopyItem(DataItem source, DataItem parent, DropResults dropResult)
      {
         DataItem newItem = source.Duplicate((dropResult & DropResults.DeepCopy) == DropResults.DeepCopy);

         return AddItem(newItem, parent, dropResult);
      }
      public DataItem AddItem(string name, string parent, string caption, string text, string image, object data)
      {
         return AddItem(name, FItems.GetItemByKey(parent != null ? parent : ""), caption, text, image, data);
      }
      public DataItem AddGroup(string name, string parent, string caption, string text, string image, object data)
      {
         return AddGroup(name, FItems.GetItemByKey(parent != null ? parent : ""), caption, text, image, data);
      }
      public DataItem AddItem(string name, DataItem parent, string caption, string text, string image, object data)
      {
         if (string.IsNullOrEmpty(name)) throw new Exception("The name of the item cannot be empty");

         DataItem newItem = new DataItem(name, caption, text, data);

         if (string.IsNullOrEmpty(image)) image = "DefaultImage";

         newItem.FImageName = image;

         if (data != null)
         {
            string typeName = data.GetType().Name;
            if (!FAllowedDropTypes.Contains(typeName)) FAllowedDropTypes.Add(typeName);
            if (string.IsNullOrEmpty(FPrimaryDropTypeName)) FPrimaryDropTypeName = typeName;
         }

         return AddItem(newItem, parent, DropResults.AppendChild);
      }
      public DataItem AddGroup(string name, DataItem parent, string caption, string text, string image, object data)
      {
         DataItem newItem = null;
         
         if (ViewStyle == ViewStyle.Tree)
         {
            newItem = AddItem(name, parent, caption, text, image, data);
            TreeNode newNode = (TreeNode)newItem.UiElement;
            newNode.NodeFont = new Font("Tahoma", 8.25F, FontStyle.Bold, GraphicsUnit.Point, ((byte)(204)));
         }
         else
         {
            ListView listView = (ListView)FViewControl;
            ListViewGroup newGroup = new ListViewGroup(name, caption);
            newGroup.Tag = data;

            listView.Groups.Add(newGroup);
         }

         newItem.IsGroup = true;

         return newItem;
      }

      public override string ToString()
      {
         return "Name=" + Name + ", Caption=" + Caption;
      }

      #endregion

      #region IItemView Event Handlers

      private void treeView_DrawNode(object sender, DrawTreeNodeEventArgs e)
      {
         Rectangle bounds = e.Node.Bounds;
         DataItem item = e.Node.Tag as DataItem;

         int viewW = FViewControl.Width;
         int nodeX = bounds.X;
         int nodeY = bounds.Y;
         int textX = bounds.X + 2;
         int nodeH = bounds.Height;
         int nodeW = viewW - textX + 1;
         int textY = nodeY + (nodeH - FNodeTextHeight) / 2;

         bool selected = (e.State & TreeNodeStates.Selected) != 0;
         bool mouseIsOver = GetHitTestRegion(bounds, e.Node.Level, FViewControl.PointToClient(Cursor.Position)) != HitTestRegion.None;

         Font textFont = item.IsGroup ? FItemFontB :
                         (mouseIsOver && FItemsHotTrack && FUnderlineHotTracked ? FItemFontU :
                         item.Hidden ? FItemFontI : FItemFont);

         Brush textBrush = selected ? SystemBrushes.HighlightText :
                           (mouseIsOver && (!item.IsGroup && FItemsHotTrack || item.IsGroup && FGroupsHotTrack) ?
                           FHotNodeBrush : (item.Hidden ? FHiddenNodeBrush : FNodeBrush));

         Brush commentBrush = selected ? (FNodeSelectionType == NodeSelectionType.Caption ? FHotTagBrush : Brushes.White) :
                              (mouseIsOver ? FHotTagBrush : FTagBrush);

         int textW = 2;
         int textMaxW = 0;
         int commentX = 0;
         int commentW = 0;
         int strW = TextRenderer.MeasureText(item.Text, textFont).Width;
         //int tagW = TextRenderer.MeasureText(item.Comment, FItemExtraTextFont).Width;

         Image extImage = FSmallImageList.ContainsKey(item.ExtImageName) ? FSmallImageList[item.ExtImageName] : null;

         if (nodeW < 100)
         {
            textMaxW = nodeW;
            if (extImage != null) textMaxW -= extImage.Width;
            textW += strW <= textMaxW ? strW : textMaxW;
         }
         else if (extImage != null)
         {
            textMaxW = (int)(nodeW * 0.7F);
            textMaxW -= extImage.Width;
            textW += strW <= textMaxW ? strW : textMaxW;
            commentX = textX + textW - 1;
            commentW = nodeW - textW - (strW <= textMaxW ? extImage.Width : 0) + 7;
         }
         else
         {
            textMaxW = (int)(nodeW * 0.7F);
            textW += strW <= textMaxW ? strW : textMaxW;
            commentX = textX + textW - 1;
            commentW = nodeW - textW + 7;
         }

         e.Graphics.FillRectangle(FClearingBrush, 0, nodeY, viewW, nodeH);

         if ((e.State & TreeNodeStates.Selected) != 0)
         {
            Rectangle r = Rectangle.Empty;
            LinearGradientBrush brush = null;
            ColorBlend blend = new ColorBlend(2);
            Color darkHighlightColor = !FViewControl.Focused ? FDarkInactiveHighlightColor : FDarkActiveHighlightColor;
            Color lightHighlightColor = !FViewControl.Focused ? FLightInactiveHighlightColor : FLightActiveHighlightColor;

            blend.Colors = new Color[] { lightHighlightColor, darkHighlightColor };
            blend.Positions = new float[] { 0.0f, 1.0f };

            if (FNodeSelectionType == NodeSelectionType.FullRow)
            {
               r = new Rectangle(1, nodeY, viewW - 2, nodeH - 1);
            }
            else if (FNodeSelectionType == NodeSelectionType.Content)
            {
               r = new Rectangle(nodeX, nodeY, nodeW, nodeH - 1);
            }
            else
            {
               r = new Rectangle(nodeX, nodeY, nodeW, nodeH - 1);
            }
            brush = new LinearGradientBrush(r, lightHighlightColor, darkHighlightColor, LinearGradientMode.Vertical);
            brush.InterpolationColors = blend;
            e.Graphics.FillRectangle(brush, r);

            DrawEdges(e.Graphics, r);

            brush.Dispose();
         }

         e.Graphics.DrawString(item.Text, textFont, textBrush, new Rectangle(textX, textY, textW, nodeH), FNodeStringFormat);

         if (commentW > 0 && !string.IsNullOrEmpty(item.Comment))
         {
            e.Graphics.DrawString(item.Comment, FItemExtraTextFont, commentBrush, new Rectangle(commentX, textY, commentW, nodeH), FNodeStringFormat);
         }

         Image image = null;
         
         if (item.IsGroup)
         {
            image = e.Node.IsSelected && FNodeSelectionType == NodeSelectionType.FullRow ?
                    (item.Expanded ? FSelectedMinusImage : FSelectedPlusImage) :
                    (item.Expanded ? FMinusImage : FPlusImage);
         }
         else
         {
            string imageName = FFlashItemTimer.Enabled && FFlashingItems.Contains(item) ?
                               item.FAnimationImages[item.FCurrentImageIndex] : item.ImageName;

            image = FSmallImageList.ContainsKey(imageName) ? FSmallImageList[imageName] : FItemImage;
         }

         FImageRect.Y = FImageRect.Height - image.Height > 0 ?
                        -1 + nodeY + (FImageRect.Height - image.Height) / 2 : nodeY - 1;

         FImageRect.X = FImageRect.Width - image.Width > 0 ?
                        nodeX - FImageRect.Width + (FImageRect.Width - image.Width) / 2 :
                        nodeX - FImageRect.Width;

         if (FImageRect.Width - image.Width < 0) e.Graphics.DrawImage(image, FImageRect);
         else e.Graphics.DrawImageUnscaled(image, FImageRect);

         if (selected && extImage != null)
         {
            FExtImageRect.Y = nodeY - 1;
            FExtImageRect.X = viewW - FExtImageRect.Width;

            e.Graphics.DrawImageUnscaled(extImage, FExtImageRect);
         }

         if (FShowFocusRectangle && (e.State & TreeNodeStates.Focused) != 0)
         {
            //using (Pen focusPen = new Pen(Color.FromArgb(200, 194, 170)))
            if (FFocusPen == null) FFocusPen = new Pen(SystemColors.ControlText);

            FFocusPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

            if (FNodeSelectionType == NodeSelectionType.FullRow)
            {
               e.Graphics.DrawRectangle(FFocusPen, 1, textY - 2, viewW - 3, nodeH - 2);
            }
            else if (FNodeSelectionType == NodeSelectionType.Content)
            {
               e.Graphics.DrawRectangle(FFocusPen, textX - 2, textY - 2, nodeW - 1, nodeH - 2);
            }
            else if (FNodeSelectionType == NodeSelectionType.Caption)
            {
               e.Graphics.DrawRectangle(FFocusPen, textX - 2, textY - 2, textW - 1, nodeH - 2);
            }
         }
      }

      private void flashTimer_Tick(object sender, EventArgs e)
      {
         foreach (DataItem item in FFlashingItems)
         {
            item.FCurrentImageIndex++;
            if (item.FCurrentImageIndex == item.FAnimationImages.Count) item.FCurrentImageIndex = 0;
         }

         if (FRedrawAllowed) FViewControl.Refresh();
      }
      private void panelHeader_Paint(object sender, PaintEventArgs e)
      {
         if (HeaderPaint != null) HeaderPaint(sender, e);
      }
      private void treeView_Click(object sender, EventArgs e)
      {
         // Does not work outside TreeNode
      }
      private void treeView_KeyDown(object sender, KeyEventArgs e)
      {
         if (!FAllowDelete || ((TreeView)sender).SelectedNode == null) return;

         if (e.KeyCode == Keys.Delete || (e.Control && e.KeyCode == Keys.D))
         {
            RemoveItem((DataItem)((TreeView)sender).SelectedNode.Tag);
         }
      }
      private void panelView_Paint(object sender, PaintEventArgs e)
      {
         try
         {
            if (!FViewControl.Visible && FViewControlBitmap != null)
            {
               e.Graphics.DrawImage(FViewControlBitmap, 0, 0);
               FViewControlBitmap.Dispose();
               FViewControlBitmap = null;
            }
         }
         catch (Exception error)
         {
            MessageBox.Show(error.ToString());
         }
      }
      private void treeView_DoubleClick(object sender, EventArgs e)
      {
         DataItem itemData = null;

         if (ViewStyle == ViewStyle.Tree)
         {
            TreeNode node = ((TreeView)FViewControl).SelectedNode;

            itemData = (node == null ? null : (DataItem)node.Tag);
         }
         else
         {
            ListViewItem item = ((ListView)FViewControl).SelectedItems.Count != 0 ? ((ListView)FViewControl).SelectedItems[0] : null;

            itemData = (item == null ? null : (DataItem)item.Tag);
         }

         if (itemData != null)
         {
            if (ItemActivated != null) ItemActivated(this, new ItemViewBasicEventArgs(itemData.IsOriginal ? itemData : itemData.Original));
         }

         // (DoubleClick does not work outside TreeNode)
      }
      private void itemView_DragOver(object sender, DragEventArgs e)
      {
         if (!AllowDrop) return;

         DragDropInfo dropInfo = ValidateDroppingData(e.Data);
         
         e.Effect = DragDropEffects.None;

         if (dropInfo == null) return;

         Point point = ((Control)sender).PointToClient(new Point(e.X, e.Y));
         FItemUnderMouse = GetItemAt(sender, point.X, point.Y);
         TreeNode targetNode = (TreeNode)FItemUnderMouse;

         if (targetNode != null)
         {
            ((TreeView)FViewControl).SelectedNode = targetNode;
            if (!FIsDraggingNow) targetNode.Expand();
         }

         if (!ValidateNodesForDragDropOperation((TreeNode)dropInfo.DraggingItem.UiElement, targetNode)) return;

         if ((e.KeyState & (8 + 32)) == (8 + 32) &&
             (e.AllowedEffect & DragDropEffects.Link) == DragDropEffects.Link)
         {
            // KeyState 8 + 32 = CTL + ALT
            e.Effect = DragDropEffects.Link;
         }
         else if ((e.KeyState & 32) == 32 &&
             (e.AllowedEffect & DragDropEffects.Link) == DragDropEffects.Link)
         {
            // ALT KeyState for link.
            e.Effect = DragDropEffects.Link;
         }
         else if ((e.KeyState & 4) == 4 &&
           (e.AllowedEffect & DragDropEffects.Move) == DragDropEffects.Move)
         {
            // SHIFT KeyState for move.
            if (dropInfo.SourceControl != this || FAllowMove) e.Effect = DragDropEffects.Move;
         }
         else if ((e.KeyState & 8) == 8 &&
           (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy)
         {
            // CTL KeyState for copy.
            e.Effect = DragDropEffects.Copy;
         }
         else if ((e.AllowedEffect & DragDropEffects.Move) == DragDropEffects.Move)
         {
            if (dropInfo.SourceControl != this || FAllowMove) e.Effect = DragDropEffects.Move;
         }
      }
      private void itemView_DragDrop(object sender, DragEventArgs e)
      {
         if (!AllowDrop) return;

         DragDropInfo dropInfo = ValidateDroppingData(e.Data);

         if (dropInfo == null)
         {
            e.Effect = DragDropEffects.None;
            return;
         }

         TreeNode targetNode = (TreeNode)FItemUnderMouse;

         if (!ValidateNodesForDragDropOperation((TreeNode)dropInfo.DraggingItem.UiElement, targetNode))
         {
            e.Effect = DragDropEffects.None;
            return;
         }

         dropInfo.TargetControl = this;
         ItemView targetView = (ItemView)dropInfo.TargetControl;
         ItemView sourceView = (ItemView)dropInfo.SourceControl;
         
         DataItem sourceItem = dropInfo.DraggingItem;
         DataItem targetItem = dropInfo.TargetItem = (targetNode != null ? (DataItem)targetNode.Tag : null);

         if (targetItem == null || targetItem.IsGroup)
         {
            dropInfo.DropResult = DropResults.AppendChild;
         }
         else
         {
            dropInfo.DropResult = DropResults.InsertBefore;
         }

         if (sourceView.PrimaryDropTypeName == targetView.PrimaryDropTypeName)
         {
            dropInfo.DropResult |= DropResults.DeepCopy;
         }

         dropInfo.DragDropEffect = e.Effect;

         if (ItemDropping != null)
         {
            ItemDropping(this, new ItemViewDragDropEventArgs(dropInfo));
         }
         else
         {
            if ((dropInfo.DropResult & DropResults.Ask) == DropResults.Ask)
            {
               // Show ask menu
            }

            if (dropInfo.DragDropEffect == DragDropEffects.Move)
            {
               ItemViewMovingEventArgs args = null;

               if (ItemMoving != null) ItemMoving(this, args = new ItemViewMovingEventArgs(sourceItem, targetItem));

               if (args != null && args.Cancel) return;

               MoveItem(sourceItem, targetItem, dropInfo.DropResult);
            }
            else if (dropInfo.DragDropEffect == DragDropEffects.Copy)
            {
               CopyItem(sourceItem, targetItem, dropInfo.DropResult);
            }
            else
            {
               e.Effect = dropInfo.DragDropEffect = DragDropEffects.None;
            }
         }
      }
      private void treeView_MouseMove(object sender, MouseEventArgs e)
      {
         //TreeView treeView = (TreeView)sender;
         //TreeNode node = treeView.GetNodeAt(e.X, e.Y);

         //if (node != null)
         //{
         //   DataItem item = node.Tag as DataItem;
         //}
      }
      private void treeView_MouseDown(object sender, MouseEventArgs e)
      {
         if (!(e.Button == MouseButtons.Left || e.Button == MouseButtons.Right)) return;

         TreeView treeView = (TreeView)sender;
         TreeNode node = treeView.GetNodeAt(e.X, e.Y);

         if (node == null)
         {
            treeView.SelectedNode = null;
            OnMouseDown(e);
         }
         else
         {
            DataItem item = node.Tag as DataItem;
            HitTestRegion hitRegion = GetHitTestRegion(node.Bounds, node.Level, e.Location);

            if (item != null && (hitRegion == HitTestRegion.LeftImage || hitRegion == HitTestRegion.PlusMinus))
            {
               item.Expanded = !item.Expanded;
            }
            if (hitRegion != HitTestRegion.None)
            {
               if (node == treeView.SelectedNode)
               {
                  //if (e.Button == MouseButtons.Left) treeView.LabelEdit = true;
               }
               else
               {
                  treeView.SelectedNode = node;
               }
            }
            if (e.Button == MouseButtons.Right)
            {
               if (ItemContextMenu != null) ItemContextMenu(this, new ItemViewBasicEventArgs(item));
            }
         }
      }
      private void treeView_MouseUp(object sender, MouseEventArgs e)
      {
         if (e.Button != MouseButtons.Left) return;

         TreeView treeView = (TreeView)sender;
         TreeNode node = treeView.GetNodeAt(e.X, e.Y);

         if (node != null)
         {
            HitTestRegion hitRegion = GetHitTestRegion(node.Bounds, node.Level, e.Location);

            if (hitRegion == HitTestRegion.LeftImage || hitRegion == HitTestRegion.PlusMinus)
            {
               FireExpandedCollapsedEvent(node, true);
            }
         }
      }
      private void itemView_ItemDrag(object sender, ItemDragEventArgs e)
      {
         if (!FAllowDrag) return;

         DragDropInfo dragInfo = new DragDropInfo(this, (DataItem)((TreeNode)e.Item).Tag, e.Button);

         FIsDraggingNow = true;

         if (ItemDragging != null) ItemDragging(this, new ItemViewDragDropEventArgs(dragInfo));

         DragDropEffects dropEffect = FViewControl.DoDragDrop(dragInfo, dragInfo.DragDropEffect);

         FIsDraggingNow = false;

         if (FAllowRemove && dragInfo.SourceControl != dragInfo.TargetControl && ((dropEffect & DragDropEffects.Move) == DragDropEffects.Move))
         {
            RemoveItem(dragInfo.DraggingItem);
         }
      }
      private void treeView_AfterSelect(object sender, TreeViewEventArgs e)
      {
         FireSelectedEvent(e.Node);
      }
      private void treeView_AfterExpand(object sender, TreeViewEventArgs e)
      {
         DataItem item = e.Node.Tag as DataItem;

         if (e.Action == TreeViewAction.Expand && _FirstNodeNeedRedrawCount > 0)
         {
            _FirstNodeNeedRedrawCount--;

            Refresh(e.Node.TreeView.Nodes[0].Tag as DataItem);
         }

         // This code doesn't work - e.Action will never be ByMouse or ByKeyboard here, but Expand or Collapse

         //FireExpandedCollapsedEvent(e.Node, e.Action == TreeViewAction.ByMouse ||
         //                                   e.Action == TreeViewAction.ByKeyboard);
      }
      private void treeView_MouseDoubleClick(object sender, MouseEventArgs e)
      {
         // Works strange
      }
      private void treeView_BeforeSelect(object sender, TreeViewCancelEventArgs e)
      {
         TreeView treeView = (TreeView)sender;
         if (treeView.SelectedNode != e.Node && treeView.SelectedNode != null) FireDeselectedEvent(treeView.SelectedNode);
      }
      private void treeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
      {
         e.Node.TreeView.LabelEdit = false;
      }
      private void treeView_NodeMouseHover(object sender, TreeNodeMouseHoverEventArgs e)
      {
         if (ItemHover != null) ItemHover(this, new ItemViewBasicEventArgs(e.Node.Tag as DataItem));
      }
      private void treeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
      {
         FireExpandedCollapsedEvent(e.Node, true);
      }

      #endregion
   }

   public class DataItem
   {
      #region DataItem Data

      string FName;
      string FText;
      bool FHidden;
      bool FIsGroup;
      bool FExpanded;
      string FComment;
      ListDictionary FData;
      internal ItemView FView;
      internal object FUiElement;
      internal string FImageName;
      internal DataItem FOriginal;
      internal string FExtImageName;
      internal int FBeginUpdateCount;
      internal int FCurrentImageIndex;
      Collection<DataItem> FDuplicates;
      internal StringCollection FAnimationImages;

      #endregion

      public DataItem(DataItem source)
      {
         FName = source.Name;
         FIsGroup = source.IsGroup;
         FText = source.Text;
         FImageName = source.FImageName;
         FExtImageName = source.FExtImageName;
         FCurrentImageIndex = source.FCurrentImageIndex;

         FData = new ListDictionary();
         FAnimationImages = new StringCollection();

         foreach (DictionaryEntry item in source.FData) FData[item.Key] = item.Value;
         foreach (string image in source.FAnimationImages) FAnimationImages.Add(image);
      }
      public DataItem(string name, string caption, string text, object data)
      {
         FName = name;
         FComment = text;
         FImageName = "";
         FExtImageName = "";
         FText = caption;
         FData = new ListDictionary();
         FAnimationImages = new StringCollection();

         if (data != null) FData[data.GetType().Name] = data;
      }

      public string Name
      {
         get
         {
            return FName;
         }
         internal set
         {
            if (string.IsNullOrEmpty(value)) throw new ArgumentNullException("DataItem.Name");

            FName = value;
         }
      }
      public string Text
      {
         get
         {
            return FText;
         }
         set
         {
            if (FText == value) return;

            FText = value;

            if (FView != null) FView.Refresh(this);

            //((TreeNode)FUiElement).Text = FText;

            if (FDuplicates != null) foreach (var item in FDuplicates) item.Text = value;
         }
      }
      public bool Hidden
      {
         get
         {
            return FHidden;
         }
         set
         {
            if (FHidden == value) return;

            FHidden = value;

            if (FView != null) FView.Refresh(this);

            if (FDuplicates != null) foreach (var item in FDuplicates) item.Hidden = value;
         }
      }
      public bool IsGroup
      {
         get
         {
            return FIsGroup;
         }
         set
         {
            FIsGroup = value;

            if (FView != null) FView.Refresh(this);
         }
      }
      public ItemView View
      {
         get
         {
            return FView;
         }
         set
         {
            if (FView == value) return;

            if (FView != null && value != null) throw new Exception("Owner already defined for this item.");

            FView = value;
         }
      }
      public bool Expanded
      {
         get
         {
            TreeNode node = (TreeNode)FUiElement;

            if (node.Nodes.Count > 0) FExpanded = node.IsExpanded;

            return FExpanded;
         }
         set
         {
            TreeNode node = (TreeNode)FUiElement;

            FExpanded = value;

            if (FView != null && FIsGroup)
            {
               if (node.Nodes.Count == 0)
               {
                  FView.Refresh(this);
               }
            }
         }
      }
      public int ChildCount
      {
         get
         {
            return UiElement != null ? (UiElement as TreeNode).Nodes.Count : -1;
         }
      }
      public string Comment
      {
         get
         {
            return FComment;
         }
         set
         {
            if (FComment == value) return;

            FComment = value;

            if (FView != null) FView.Refresh(this);

            if (FDuplicates != null) foreach (var item in FDuplicates) item.Comment = value;
         }
      }
      public bool IsOriginal
      {
         get
         {
            return FOriginal == null || FOriginal == this;
         }
      }
      public object UiElement
      {
         get
         {
            return FUiElement;
         }
         set
         {
            if (FUiElement == value) return;

            if (FUiElement != null && value != null) throw new Exception("UiElement already defined for this item.");

            FUiElement = value;
         }
      }
      public DataItem Parent
      {
         get
         {
            return ((TreeNode)FUiElement).Parent != null ? (DataItem)((TreeNode)FUiElement).Parent.Tag : null;
         }
      }
      public string ImageName
      {
         get
         {
            return FImageName;
         }
         set
         {
            if (FImageName == value) return;

            FImageName = value;

            if (FView != null) FView.Refresh(this);

            if (FDuplicates != null) foreach (var item in FDuplicates) item.ImageName = value;
         }
      }
      public DataItem Original
      {
         get
         {
            return FOriginal;
         }
         set
         {
            if (FOriginal == value) return;

            if (FOriginal != null && value != null) throw new Exception("Original already defined for this item.");

            FOriginal = value;
         }
      }
      public string ExtImageName
      {
         get
         {
            return FExtImageName;
         }
         set
         {
            if (FExtImageName == value) return;

            FExtImageName = value;

            if (FView != null) FView.Refresh(this);

            if (FDuplicates != null) foreach (var item in FDuplicates) item.ExtImageName = value;
         }
      }
      public object this[Type type]
      {
         get
         {
            object @object = FData[type.Name];

            if (@object != null) return @object;

            foreach (object item in FData.Values)
            {
               if (item != null && item.GetType().IsSubclassOf(type)) return item;
            }
            return null;
         }
         set
         {
            FData[type.Name] = value;
         }
      }
      public object this[string key]
      {
         get
         {
            return FData[key];
         }
         set
         {
            FData[key] = value;
         }
      }
      public Collection<DataItem> Duplicates
      {
         get
         {
            if (FDuplicates == null) FDuplicates = new Collection<DataItem>();

            return FDuplicates;
         }
      }

      #region IDataItem Members

      public DataItem Clone()
      {
         DataItem srcCurItem = null;
         DataItem newCurItem = null;

         TreeNode srcCurNode = (TreeNode)UiElement;
         TreeNode newCurNode = (TreeNode)srcCurNode.Clone();

         TreeNode newRootNode = newCurNode;

         int level = newRootNode.Level;

         for (;;)
         {
            srcCurItem = (DataItem)srcCurNode.Tag;
            newCurItem = (DataItem)srcCurItem.Duplicate(false);

            newCurNode.Tag = newCurItem;
            newCurItem.FUiElement = newCurNode;

            newCurNode = ItemView.GetNextNode(newCurNode, level);
            if (newCurNode == null) break;

            srcCurNode = ItemView.GetNextNode(srcCurNode, level);
            if (srcCurNode == null) break;
         }

         return (DataItem)newRootNode.Tag;
      }
      public DataItem Duplicate(bool deep)
      {
         if (deep)
         {
            return Clone();
         }
         else
         {
            return new DataItem(this);
         }
      }

      #endregion

      public override string ToString()
      {
         return "Name=" + Name + ", Text=" + Text;
      }
   }


   public enum ViewStyle
   {
      None,
      Tree,
      List,
      Icons,
      Details,
      Thumbnails
   }

   public enum DropResults
   {
      None = 0,
      Ask = 1,
      Auto = 2,
      Replace = 4,
      DeepCopy = 8,
      AppendChild = 16,
      PrependChild = 32,
      InsertAfter = 64,
      InsertBefore = 128
   }

   public enum HitTestRegion
   {
      None,
      Text,
      Comment,
      PlusMinus,
      LeftImage,
      RightImage,
   }

   public enum NodeSelectionType
   {
      Caption,
      Content,
      FullRow
   }

   public enum FocusRectangleType
   {
      None,
      Weak,
      Strong
   }


   public class DragDropInfo
   {
      public MouseButtons Button;

      public DataItem TargetItem;
      public Control TargetControl;

      public DataItem DraggingItem;
      public Control SourceControl;

      public DropResults DropResult;
      public DragDropEffects DragDropEffect;

      public DragDropInfo(Control sourceControl, DataItem draggingItem, MouseButtons button)
      {
         Button = button;
         DraggingItem = draggingItem;
         SourceControl = sourceControl;
         DropResult = DropResults.InsertBefore;
         DragDropEffect = DragDropEffects.All | DragDropEffects.Link;
      }
   }

   public class ItemsCompareEventArgs : EventArgs
   {
      public DataItem Item1;
      public DataItem Item2;
      public int CompareResult;

      public ItemsCompareEventArgs()
      {
      }
   }

   public class ItemViewBasicEventArgs : EventArgs
   {
      public DataItem Item;

      public ItemViewBasicEventArgs(DataItem item)
      {
         Item = item;
      }
   }

   public class ItemViewCancelEventArgs : EventArgs
   {
      public bool Cancel;

      public DataItem Item;

      public ItemViewCancelEventArgs(DataItem item)
      {
         Item = item;
      }
   }

   public class ItemViewDragDropEventArgs : EventArgs
   {
      public DragDropInfo DragDropInfo;

      public ItemViewDragDropEventArgs(DragDropInfo info)
      {
         DragDropInfo = info;
      }
   }

   public class ItemViewXmlAssignEventArgs : EventArgs
   {
      public string Name;
      public string Text;
      public object Data;
      public bool Hidden;
      public bool IsGroup;
      public bool SkipItem;
      public object Context;
      public string Caption;
      public string ImageName;
      public XElement XElement;
      public string NameAttrId;
      public string ExtImageName;
      public string CaptionAttrId;
      public DataItem ParentItem;

      public ItemViewXmlAssignEventArgs()
      {
      }
   }

   public class ItemViewXmlRetrieveEventArgs : EventArgs
   {
      public string Name;
      public string XName;
      public bool SkipItem;
      public bool Expanded;
      public DataItem Item;
      public string Caption;
      public XElement XElement;
      public string NameAttrId;
      public string CaptionAttrId;

      public ItemViewXmlRetrieveEventArgs()
      {
      }
   }

   public class ItemViewMovedEventArgs : ItemViewBasicEventArgs
   {
      public readonly DataItem OldParent;

      public ItemViewMovedEventArgs(DataItem item, DataItem oldParent)
         : base(item)
      {
         OldParent = oldParent;
      }
   }

   public class ItemViewMovingEventArgs : ItemViewCancelEventArgs
   {
      public readonly DataItem NewParent;

      public ItemViewMovingEventArgs(DataItem item, DataItem newParent)
         : base(item)
      {
         NewParent = newParent;
      }
   }

   public class ItemViewRemovingEventArgs : ItemViewCancelEventArgs
   {
      public bool WithChildren;

      public DataItem NewChildrenParent;

      public ItemViewRemovingEventArgs(DataItem item)
         : base(item)
      {
         Item = item;
         WithChildren = true;
         NewChildrenParent = null;
      }
   }

   public class ItemViewExpandedCollapsedEventArgs : ItemViewBasicEventArgs
   {
      public readonly bool ByUser;

      public ItemViewExpandedCollapsedEventArgs(DataItem item, bool byUser)
         : base(item)
      {
         ByUser = byUser;
      }
   }


   class NodeSorter : IComparer
   {
      public int Compare(object x, object y)
      {
         DataItem item1 = ((TreeNode)x).Tag as DataItem;
         DataItem item2 = ((TreeNode)y).Tag as DataItem;

         ItemView view = item1.View;

         return view.FireItemsCompareEvent(item1, item2);
      }
   }

   class TreeNodeKeyedCollection : KeyedCollection<string, TreeNode>
   {
      public TreeNodeKeyedCollection()
         : base()
      {

      }

      protected override string GetKeyForItem(TreeNode item)
      {
         return item.Name;
      }

      public TreeNode GetItemByKey(string key)
      {
         TreeNode item = null;
         if (Dictionary != null) Dictionary.TryGetValue(key, out item);
         return item;
      }
   }

   public class ItemViewKeyedCollection : KeyedCollection<string, ItemView>
   {
      public ItemViewKeyedCollection()
         : base()
      {

      }

      protected override string GetKeyForItem(ItemView item)
      {
         return item.Name;
      }

      public ItemView GetItemByKey(string key)
      {
         ItemView item = null;
         if (Dictionary != null) Dictionary.TryGetValue(key, out item);
         return item;
      }
   }

   public class DataItemKeyedCollection : KeyedCollection<string, DataItem>
   {
      public DataItemKeyedCollection()
         : base()
      {

      }

      protected override string GetKeyForItem(DataItem item)
      {
         return item.Name;
      }

      public DataItem GetItemByKey(string key)
      {
         DataItem item = null;
         if (Dictionary != null) Dictionary.TryGetValue(key, out item);
         return item;
      }

      internal void RenameItem(DataItem item, string newName)
      {
         ChangeItemKey(item, newName);
      }
   }

   class ListViewGroupKeyedCollection : KeyedCollection<string, ListViewGroup>
   {
      public ListViewGroupKeyedCollection()
         : base()
      {

      }

      protected override string GetKeyForItem(ListViewGroup item)
      {
         return item.Name;
      }

      public ListViewGroup GetItemByKey(string key)
      {
         ListViewGroup item = null;
         if (Dictionary != null) Dictionary.TryGetValue(key, out item);
         return item;
      }
   }

   class SplitContainerKeyedCollection : KeyedCollection<string, SplitContainer>
   {
      public SplitContainerKeyedCollection()
         : base()
      {

      }

      protected override string GetKeyForItem(SplitContainer item)
      {
         return item.Name;
      }

      public SplitContainer GetItemByKey(string key)
      {
         SplitContainer item = null;
         if (Dictionary != null) Dictionary.TryGetValue(key, out item);
         return item;
      }
   }
}